﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Diagnostics;
using DynORM.Interfaces.BLLMapper.Enum;
using DynORM.BLLMapper;
using DynORM.Enums;
using DynORM.Interfaces;
using System.Linq;
using DynORM.Mappers;
using DynORM.ReferenceObjects;

namespace UnitTestProject
{
    [TestClass]
    public class BLLAdaptiveMapperPredicateUnitTests_PostgreSQL
    {
        #region db creation and clean up
        //these two members are used only in CreateDB and DropDB methods
        //production code and test methods take this setting from the config file
        const DBMSTypes dbType = DBMSTypes.PostgreSQL;
        static string dbName = "TestDB";
        static string pooling = "Pooling=false;";//otherwise it won't let drop db (db will be in use with connections in pool)
        static string connectionStringToDatabase = "Server=127.0.0.1;Port=5432;User Id=postgres;Password=postgres;Database=" + dbName + ";" + pooling;
        static string connectionStringToServer = "Server=127.0.0.1;Port=5432;User Id=postgres;Password=postgres;" + pooling;

        [TestInitialize]
        public void TestInit()
        {
            DatabaseMapper.CreateDatabase(connectionStringToServer, dbType, new Database(dbName), true, false);
        }

        [TestCleanup]
        public void TestClean()
        {
            DatabaseMapper.DropDatabase(connectionStringToServer, dbType, dbName);
        }

        #endregion


        public class TestBLL : BLLSuperType
        {
            [PrimaryKeyAttribute("RowID", 0)]
            public Guid ID { get; set; }

            [FieldAttribute(Attributes=new object[] { 12, 5 })]
            public decimal SomeProperty1 { get; set; }

            [FieldAttribute]
            public DateTime SomeProperty2 { get; set; }

            [FieldAttribute]
            public int SomeProperty3 { get; set; }
        }



        [TestMethod]
        public void RetrieveWithPredicateEqualInt()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid,TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for(int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty3"), BLLComparisonOperator.Equal, 3));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(1, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateNotEqualInt()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty3"), BLLComparisonOperator.NotEqual, 3));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(9, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateEqualDateTime()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Today.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty2"), BLLComparisonOperator.Equal, DateTime.Today));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(1, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateNotEqualDateTime()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Today.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty2"), BLLComparisonOperator.NotEqual, DateTime.Today));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(9, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateEqualDecimal()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Today.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty1"), BLLComparisonOperator.Equal, 0.5m));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(1, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateNotEqualDecimal()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Today.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty1"), BLLComparisonOperator.NotEqual, 0.5m));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(9, retrieved.Count());

            mapper.DeleteBatch(batch);
        }




        [TestMethod]
        public void RetrieveWithPredicateLessInt()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty3"), BLLComparisonOperator.Less, 3));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(3, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateGreaterInt()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty3"), BLLComparisonOperator.Greater, 3));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(6, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateLessOrEqualInt()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty3"), BLLComparisonOperator.LessOrEqual, 3));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(4, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateGreaterOrEqualInt()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty3"), BLLComparisonOperator.GreaterOrEqual, 3));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(7, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateLessDateTime()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);
            DateTime now = DateTime.Now;

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty2"), BLLComparisonOperator.Less, now.AddDays(3)));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(3, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateGreaterDateTime()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);
            DateTime now = DateTime.Now;

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty2"), BLLComparisonOperator.Greater, now.AddDays(3)));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(6, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateLessDecimal()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty1"), BLLComparisonOperator.Less, 3.5m));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(3, retrieved.Count());

            mapper.DeleteBatch(batch);
        }

        [TestMethod]
        public void RetrieveWithPredicateGreaterDecimal()
        {
            //Assign
            Dictionary<Guid, TestBLL> objects = new Dictionary<Guid, TestBLL>();
            IBLLAdaptiveMapper mapper = new BLLAdaptiveMapper(connectionStringToDatabase, dbType);

            for (int i = 0; i < 10; i++)
            {
                Guid id = Guid.NewGuid();
                TestBLL bllObj = (TestBLL)mapper.Create(typeof(TestBLL), id);
                bllObj.SomeProperty1 = (decimal)i + 0.5m;
                bllObj.SomeProperty2 = DateTime.Now.AddDays(i);
                bllObj.SomeProperty3 = i;
                bllObj.MarkAsLoaded();
                objects.Add(id, bllObj);
            }

            TestBLL[] batch = new TestBLL[10];
            objects.Values.CopyTo(batch, 0);
            mapper.WriteBatch(batch, true);

            //Act
            List<BLLComparison> predicate = new List<BLLComparison>();
            predicate.Add(new BLLComparison(typeof(TestBLL).GetProperty("SomeProperty1"), BLLComparisonOperator.Greater, 3.5m));
            var retrieved = mapper.Read(typeof(TestBLL), predicate);

            //Assert
            Assert.AreEqual<int>(6, retrieved.Count());

            mapper.DeleteBatch(batch);
        }
    }
}
